<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>调用链详情 - 默认展开第二层</title>
    <!-- 引入依赖 -->
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <link rel="stylesheet" href="https://unpkg.com/element-plus/dist/index.css">
    <script src="https://unpkg.com/element-plus"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

    <style>
        /* 保持原有样式不变 */
        body {
            margin: 0;
            padding: 20px;
            background-color: #f5f7fa;
            font-family: "Helvetica Neue", Arial, sans-serif;
        }

        .container {
            max-width: 1400px;
            margin: 0 auto;
        }

        .query-form {
            background: #fff;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08);
            margin-bottom: 20px;
        }

        .form-row {
            display: flex;
            flex-wrap: wrap;
            gap: 16px;
            margin-bottom: 16px;
        }

        .form-item {
            flex: 1;
            min-width: 200px;
        }

        .table-container {
            background: #fff;
            border-radius: 8px;
            box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08);
            padding: 20px;
        }

        /* 树形展示核心样式 - 无连线设计 */
        .tree-dialog .el-dialog__body {
            padding: 0;
            max-height: 85vh;
            overflow: hidden;
            display: flex;
            flex-direction: column;
        }

        .tree-header {
            padding: 12px 20px;
            border-bottom: 1px solid #eee;
            background: #f9f9f9;
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-wrap: wrap;
            gap: 10px;
        }

        .tree-actions {
            display: flex;
            gap: 10px;
        }

        .tree-stats {
            font-size: 12px;
            color: #666;
            margin-top: 5px;
        }

        .tree-content {
            flex: 1;
            overflow: auto;
            padding: 20px;
            background-color: #fafafa;
        }

        .json-tree-container {
            font-size: 14px;
            line-height: 1.5;
            min-width: fit-content;
            padding-right: 20px;
        }

        /* 树形节点样式 - 无连线设计，强化缩进层级 */
        .tree-node {
            position: relative;
            padding-top: 6px;
            padding-bottom: 6px;
            transition: transform 0.2s;
        }

        .tree-node:hover {
            transform: translateX(4px);
        }

        /* 强化层级缩进差异 */
        .tree-node[data-depth="0"] {
            padding-left: 10px;
        }

        .tree-node[data-depth="1"] {
            padding-left: 40px;
        }

        .tree-node[data-depth="2"] {
            padding-left: 70px;
        }

        .tree-node[data-depth="3"] {
            padding-left: 100px;
        }

        .tree-node[data-depth="4"] {
            padding-left: 130px;
        }

        .tree-node[data-depth="5"] {
            padding-left: 160px;
        }

        .tree-node[data-depth="6"] {
            padding-left: 190px;
        }

        .node-wrapper {
            display: flex;
            align-items: center;
            gap: 12px;
            padding: 10px 15px;
            border-radius: 6px;
            transition: all 0.25s ease;
            border-left: 4px solid transparent;
        }

        /* 层级边框颜色强化 */
        .tree-node[data-depth="0"] .node-wrapper {
            border-left-color: #1890ff;
            background-color: #f0f7ff;
        }

        .tree-node[data-depth="1"] .node-wrapper {
            border-left-color: #52c41a;
            background-color: #f6ffed;
        }

        .tree-node[data-depth="2"] .node-wrapper {
            border-left-color: #faad14;
            background-color: #fffbe6;
        }

        .tree-node[data-depth="3"] .node-wrapper {
            border-left-color: #fa8c16;
            background-color: #fff2e8;
        }

        .tree-node[data-depth="4"] .node-wrapper {
            border-left-color: #f5222d;
            background-color: #fff1f0;
        }

        .tree-node[data-depth="5"] .node-wrapper {
            border-left-color: #722ed1;
            background-color: #f9f0ff;
        }

        .tree-node[data-depth="6"] .node-wrapper {
            border-left-color: #13c2c2;
            background-color: #e6fffb;
        }

        /* 悬浮效果强化 */
        .node-wrapper:hover {
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
            transform: translateY(-1px);
        }

        .node-wrapper:hover .node-title {
            color: #1890ff;
            font-weight: 600;
        }

        .node-icon {
            width: 18px;
            height: 18px;
            border-radius: 50%;
            flex-shrink: 0;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
        }

        .node-icon-success {
            background-color: #52c41a;
        }

        .node-icon-error {
            background-color: #f5222d;
        }

        .node-icon-sql {
            background-color: #1890ff;
        }

        .node-main {
            flex: 1;
            min-width: 0; /* 解决flex子元素文本溢出问题 */
        }

        .node-title {
            font-weight: 500;
            color: #333;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            max-width: 600px;
            transition: color 0.2s, font-weight 0.2s;
        }

        .node-meta {
            font-size: 12px;
            color: #666;
            margin-top: 4px;
            display: flex;
            flex-wrap: wrap;
            gap: 15px;
        }

        .meta-item {
            display: flex;
            align-items: center;
            gap: 5px;
        }

        .meta-key {
            color: #888;
            font-weight: 500;
        }

        .meta-value {
            color: #333;
        }

        .cost-highlight {
            color: #f5222d;
            font-weight: 500;
        }

        /* 参数按钮样式 */
        .param-btn {
            margin-left: auto; /* 靠右对齐 */
            white-space: nowrap;
        }

        /* 参数弹框样式 */
        .param-dialog .el-dialog__body {
            padding: 20px;
            max-height: 70vh;
            overflow-y: auto;
        }

        .param-container {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
        }

        .param-section {
            background-color: #fafafa;
            border-radius: 6px;
            padding: 15px;
            border: 1px solid #e5e6eb;
        }

        .param-section-title {
            font-size: 16px;
            font-weight: 500;
            color: #1890ff;
            margin-bottom: 15px;
            padding-bottom: 8px;
            border-bottom: 1px solid #e8e8e8;
        }

        .param-item {
            margin-bottom: 15px;
            padding-bottom: 15px;
            border-bottom: 1px dashed #e8e8e8;
        }

        .param-item:last-child {
            margin-bottom: 0;
            padding-bottom: 0;
            border-bottom: none;
        }

        .param-type {
            font-size: 13px;
            color: #595959;
            margin-bottom: 5px;
            font-weight: 500;
        }

        .param-value {
            font-size: 14px;
            line-height: 1.6;
            background-color: #fff;
            padding: 10px;
            border-radius: 4px;
            border: 1px solid #e5e6eb;
            white-space: pre-wrap;
            word-wrap: break-word;
            max-height: 300px;
            overflow-y: auto;
        }

        .empty-param {
            color: #999;
            text-align: center;
            padding: 20px 0;
            font-size: 14px;
        }

        /* 节点详情页面优化 */
        .detail-content {
            max-height: 60vh;
            overflow-y: auto;
            padding: 15px 0;
        }

        .descriptions-two-column {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 16px;
        }

        .descriptions-item {
            display: flex;
            margin-bottom: 4px;
        }

        .descriptions-item__label {
            flex: 0 0 130px;
            color: #595959;
            font-weight: 500;
            padding-right: 12px;
            text-align: right;
            font-size: 14px;
        }

        .descriptions-item__content {
            flex: 1;
            word-break: break-word;
            font-size: 14px;
            line-height: 1.6;
        }

        /* 复制按钮样式优化 */
        .copy-btn {
            cursor: pointer;
            color: #1890ff;
            margin-left: 8px;
            transition: color 0.2s, transform 0.2s;
            font-size: 14px;
        }

        .copy-btn:hover {
            color: #096dd9;
            transform: scale(1.1);
        }

        /* SQL容器优化 */
        .sql-section {
            margin-top: 20px;
            border-top: 1px solid #e8e8e8;
            padding-top: 16px;
        }

        .sql-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 12px;
            font-size: 14px;
            color: #595959;
            font-weight: 500;
        }

        .sql-actions {
            display: flex;
            gap: 8px;
        }

        .sql-container {
            max-height: 300px;
            overflow-y: auto;
            background-color: #fafafa;
            padding: 16px;
            border-radius: 6px;
            border: 1px solid #e5e6eb;
            line-height: 1.6;
            box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05);
        }

        .sql-content {
            white-space: pre-wrap;
            word-wrap: break-word;
            font-size: 14px;
            font-family: monospace;
            margin: 0;
            color: #333;
        }

        /* 可复制文本容器 */
        .copyable-text {
            display: flex;
            align-items: center;
            flex-wrap: wrap;
        }

        .copyable-text span {
            flex: 1;
            word-break: break-all;
        }

        /* 辅助样式 */
        .legend {
            display: flex;
            align-items: center;
            gap: 15px;
            font-size: 12px;
            flex-wrap: wrap;
        }

        .legend-item {
            display: flex;
            align-items: center;
            gap: 5px;
        }

        .empty-state {
            text-align: center;
            padding: 50px 0;
            color: #999;
        }
    </style>
</head>
<body>
<div id="app" class="container">
    <!-- 保持原有结构不变 -->
    <div class="query-form">
        <h2>方法调用链查询</h2>
        <el-form :inline="false" :model="queryParams">
            <div class="form-row">
                <el-row>
                    <!-- 服务类名 级联下拉（支持模糊搜索） -->
                    <el-col :span="8">
                        <el-form-item label="服务类名" class="form-item">
                            <el-select
                                    v-model="queryParams.serviceName"
                                    placeholder="请选择或搜索服务类名"
                                    filterable
                                    :filter-method="filterServiceName"
                                    @change="handleServiceChange"
                                    clearable
                                    style="width: 100%"
                            >
                                <el-option
                                        v-for="service in filteredServiceList"
                                        :key="service.serviceName"
                                        :label="`${service.serviceName}（${service.serviceDes || '无描述'}）`"
                                        :value="service.serviceName"
                                ></el-option>
                            </el-select>
                        </el-form-item>
                    </el-col>

                    <!-- 方法名 下拉（始终可用，支持模糊搜索，联动服务类名） -->
                    <el-col :span="8">
                        <el-form-item label="方法名" class="form-item">
                            <el-select
                                    v-model="queryParams.methodName"
                                    placeholder="请选择或搜索方法名"
                                    filterable
                                    :filter-method="filterMethodName"
                                    clearable
                                    style="width: 100%"
                            >
                                <el-option
                                        v-for="method in filteredMethodList"
                                        :key="`${method.serviceName}-${method.methodName}`"
                                        :label="`${method.methodName}（${method.serviceName} - ${method.methodDes || '无描述'}）`"
                                        :value="method.methodName"
                                ></el-option>
                            </el-select>
                        </el-form-item>
                    </el-col>

                    <el-col :span="8">
                        <el-form-item label="全局TraceId" class="form-item">
                            <el-input v-model="queryParams.traceId" placeholder="请输入全局唯一traceId"></el-input>
                        </el-form-item>
                    </el-col>
                    <el-col :span="8">
                        <el-form-item label="开始时间" class="form-item">
                            <div class="block">
                                <el-date-picker
                                        v-model="dateRange"
                                        type="datetimerange"
                                        :shortcuts="pickerOptions"
                                        range-separator="至"
                                        start-placeholder="开始日期"
                                        end-placeholder="结束日期"
                                        @change="handleDateChange"
                                        align="right">
                                </el-date-picker>
                            </div>
                        </el-form-item>
                    </el-col>
                    <el-col :span="8">
                        <el-form-item>
                            <el-button type="primary" @click="handleQuery">查询</el-button>
                            <el-button @click="resetQuery">重置</el-button>
                        </el-form-item>
                    </el-col>
                    <el-col :span="8">
                        <el-form-item>
                            <el-switch
                                    v-model="trace_monitor_enabled"
                                    active-text=""
                                    inactive-text="监控开关"
                                    @change="fetchSwitch_Set('trace-monitor-enabled')"
                                    style="margin-right: 20px">
                            </el-switch>
                            &emsp;
                            <el-switch
                                    v-model="trace_monitor_parameterSwitch"
                                    active-text=""
                                    inactive-text="方法开关"
                                    @change="fetchSwitch_Set('trace-monitor-parameterSwitch')">
                            </el-switch>
                        </el-form-item>
                    </el-col>

                </el-row>
            </div>
        </el-form>
    </div>

    <div class="table-container">
        <el-table
                :data="tableData"
                border
                style="width: 100%"
                v-loading="loading"
        >
            <el-table-column prop="traceId" label="全局TraceId" width="200"></el-table-column>
            <el-table-column prop="serviceName" label="服务类名"></el-table-column>
            <el-table-column prop="methodName" label="方法名"></el-table-column>
            <el-table-column prop="startTime" label="开始时间">
                <template #default="scope">
                    {{ formatDateTime(scope.row.startTime) }}
                </template>
            </el-table-column>
            <el-table-column prop="costTime" label="执行耗时(ms)">
                <template #default="scope">
            <span :class="scope.row.costTime > 1000 ? 'cost-highlight' : ''">
              {{ scope.row.costTime }}ms
            </span>
                </template>
            </el-table-column>
            <el-table-column label="操作" width="120">
                <template #default="scope">
                    <el-button
                            type="text"
                            size="small"
                            @click="handleViewTrace(scope.row.traceId)"
                    >
                        查看调用链
                    </el-button>
                </template>
            </el-table-column>
        </el-table>

        <div style="margin-top: 16px; text-align: right;">
            <el-pagination
                    @size-change="handleSizeChange"
                    @current-change="handleCurrentChange"
                    :current-page="currentPage"
                    :page-sizes="[10, 20, 50, 100]"
                    :page-size="queryParams.size"
                    layout="total, sizes, prev, pager, next, jumper"
                    :total="totalCount"
            ></el-pagination>
        </div>
    </div>

    <el-dialog
            title="调用链详情"
            v-model="traceDialogVisible"
            width="85%"
            class="tree-dialog"
    >
        <div class="tree-header">
            <div>
                <div class="legend">
                    <div class="legend-item">
                        <div class="node-icon node-icon-success"></div>
                        <span>成功调用</span>
                    </div>
                    <div class="legend-item">
                        <div class="node-icon node-icon-error"></div>
                        <span>失败调用</span>
                    </div>
                    <div class="legend-item">
                        <div class="node-icon node-icon-sql"></div>
                        <span>SQL执行</span>
                    </div>
                    <div class="legend-item" v-if="currentTraceId">
                        <span>TraceId: {{ currentTraceId }}</span>
                    </div>
                </div>
                <div class="tree-stats" v-if="treeStats.totalNodes > 0">
                    共 {{ treeStats.totalNodes }} 个节点（最深层级: {{ treeStats.maxDepth }}，SQL节点:
                    {{ treeStats.sqlNodes }}）
                </div>
            </div>
            <div class="tree-actions">
                <el-button type="text" size="small" @click="toggleAllExpand">
                    {{ isAllExpanded ? '全部折叠' : '全部展开' }}
                </el-button>
                <el-button type="text" size="small" @click="expandToDepth">
                    展开至第 {{ expandDepth }} 层
                </el-button>
                <el-select v-model="expandDepth" size="small" style="width: 100px;">
                    <el-option label="1层" value="1"></el-option>
                    <el-option label="2层" value="2"></el-option>
                    <el-option label="3层" value="3"></el-option>
                    <el-option label="5层" value="5"></el-option>
                    <el-option label="10层" value="10"></el-option>
                </el-select>
                <el-button type="text" size="small" @click="copyTraceId" v-if="currentTraceId">
                    复制TraceId
                </el-button>
            </div>
        </div>

        <div class="tree-content">
            <div v-if="loadingTree" class="empty-state">加载中...</div>
            <div v-else-if="treeData.length === 0" class="empty-state">无调用链数据</div>
            <div v-else class="json-tree-container">
                <template v-for="(node, index) in treeData" :key="node.spanId || `${node.id}-${index}`">
                    <tree-node
                            :node="node"
                            :is-last="index === treeData.length - 1"
                            @toggle-expand="handleNodeToggle"
                            @show-params="handleShowParams"
                    ></tree-node>
                </template>
            </div>
        </div>

        <!-- 节点详情弹窗 -->
        <el-dialog
                title="节点详情"
                v-model="nodeDetailVisible"
                width="70%"
        >
            <div style="text-align: right; margin-bottom: 10px;">
                <el-button type="text" size="small" @click="closeNodeDetail">关闭</el-button>
            </div>
            <div class="detail-content">
                <div v-if="currentNode">
                    <div class="descriptions-two-column">
                        <div class="descriptions-item">
                            <div class="descriptions-item__label">服务类名</div>
                            <div class="descriptions-item__content">
                                <div class="copyable-text">
                                    <span>{{ currentNode.serviceName }}</span>
                                    <i class="el-icon-copy copy-btn"
                                       @click="copyToClipboard(currentNode.serviceName, '服务类名')"></i>
                                </div>
                            </div>
                        </div>

                        <div class="descriptions-item">
                            <div class="descriptions-item__label">方法名</div>
                            <div class="descriptions-item__content">
                                <div class="copyable-text">
                                    <span>{{ currentNode.methodName }}</span>
                                    <i class="el-icon-copy copy-btn"
                                       @click="copyToClipboard(currentNode.methodName, '方法名')"></i>
                                </div>
                            </div>
                        </div>

                        <div class="descriptions-item">
                            <div class="descriptions-item__label">当前节点ID</div>
                            <div class="descriptions-item__content">
                                <div class="copyable-text">
                                    <span>{{ currentNode.spanId }}</span>
                                </div>
                            </div>
                        </div>

                        <div class="descriptions-item">
                            <div class="descriptions-item__label">父节点ID</div>
                            <div class="descriptions-item__content">
                                <div class="copyable-text">
                                    <span>{{ currentNode.parentSpanId || '无' }}</span>
                                </div>
                            </div>
                        </div>

                        <div class="descriptions-item">
                            <div class="descriptions-item__label">调用深度</div>
                            <div class="descriptions-item__content">
                                <div class="copyable-text">
                                    <span>{{ currentNode.depth }}</span>
                                </div>
                            </div>
                        </div>

                        <div class="descriptions-item">
                            <div class="descriptions-item__label">执行耗时</div>
                            <div class="descriptions-item__content">
                                <div class="copyable-text">
                                    <span>{{ currentNode.costTime }}ms</span>
                                </div>
                            </div>
                        </div>

                        <div class="descriptions-item">
                            <div class="descriptions-item__label">开始时间</div>
                            <div class="descriptions-item__content">
                                <div class="copyable-text">
                                    <span>{{ formatDateTime(currentNode.startTime) }}</span>
                                </div>
                            </div>
                        </div>

                        <div class="descriptions-item">
                            <div class="descriptions-item__label">结束时间</div>
                            <div class="descriptions-item__content">
                                <div class="copyable-text">
                                    <span @click="copyToClipboard(formatDateTime(currentNode.endTime), '结束时间')">{{ formatDateTime(currentNode.endTime)
                                        }}</span>
                                </div>
                            </div>
                        </div>

                        <div class="descriptions-item">
                            <div class="descriptions-item__label">线程名称</div>
                            <div class="descriptions-item__content">
                                <div class="copyable-text">
                                    <span>{{ currentNode.threadName }}</span>
                                </div>
                            </div>
                        </div>
                        <div class="descriptions-item">
                            <div class="descriptions-item__label">执行结果</div>
                            <div class="descriptions-item__content">
                                <div class="copyable-text">
                    <span :class="currentNode.success ? 'el-tag el-tag-success' : 'el-tag el-tag-danger'">
                      {{ currentNode.success ? '成功' : '失败' }}
                    </span>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="descriptions-item">
                        <div class="descriptions-item__label">节点路径</div>
                        <div class="descriptions-item__content">
                            <div class="copyable-text">
                                <span>{{ currentNode.path || '无' }}</span>
                                <i class="el-icon-copy copy-btn"
                                   @click="copyToClipboard(currentNode.path || '无', '节点路径')"></i>
                                <el-button
                                        type="primary"
                                        size="mini"
                                        icon="el-icon-copy-document"
                                        @click="copyToClipboard(currentNode.path || '无', '节点路径')"
                                >
                                    复制路径
                                </el-button>
                            </div>
                        </div>
                    </div>
                    <div class="sql-section" v-if="currentNode.fullSql || currentNode.originalSql">
                        <div class="sql-header">
                            <span>SQL语句</span>
                            <div class="sql-actions">
                                <el-button
                                        type="primary"
                                        size="mini"
                                        icon="el-icon-copy-document"
                                        @click="copyToClipboard(currentNode.fullSql || currentNode.originalSql, 'SQL语句')"
                                >
                                    复制SQL
                                </el-button>
                            </div>
                        </div>
                        <div class="sql-container">
                <pre class="sql-content">
                  {{ currentNode.fullSql || currentNode.originalSql }}
                </pre>
                        </div>
                    </div>
                </div>
            </div>
        </el-dialog>

        <!-- 参数详情弹框 -->
        <el-dialog
                title="参数详情"
                v-model="paramDialogVisible"
                width="80%"
                class="param-dialog"
        >
            <div class="param-container">
                <!-- 左侧：请求参数（type=1） -->
                <div class="param-section">
                    <div class="param-section-title">请求参数</div>
                    <div v-if="requestParams.length > 0">
                        <div class="param-item" v-for="(param, index) in requestParams" :key="`req-${index}`">
                            <div class="param-type">参数类型: {{ param.parameterTypes || '未知类型' }}</div>
                            <div class="param-value">{{ param.parameterValues || '无参数值' }}</div>
                        </div>
                    </div>
                    <div v-else class="empty-param">无请求参数</div>
                </div>

                <!-- 右侧：返回参数（type=2） -->
                <div class="param-section">
                    <div class="param-section-title">返回参数</div>
                    <div v-if="responseParam">
                        <div class="param-item">
                            <div class="param-type">返回类型: {{ responseParam.parameterTypes || '未知类型' }}</div>
                            <div class="param-value">{{ responseParam.parameterValues || '无返回值' }}</div>
                        </div>
                    </div>
                    <div v-else class="empty-param">无返回参数</div>
                </div>
            </div>
        </el-dialog>
    </el-dialog>
</div>

<script>
    const {createApp, ref, reactive, onMounted, defineComponent, h} = Vue;
    const {
        ElForm, ElFormItem, ElInput, ElButton,
        ElTable, ElTableColumn, ElPagination, ElDialog,
        ElDescriptions, ElDescriptionsItem, ElTag, ElMessage,
        ElSelect, ElOption, ElDatePicker
    } = ElementPlus;

    // 树形节点组件 - 所有有参数的节点均显示参数按钮
    const TreeNode = defineComponent({
        name: 'TreeNode',
        props: {
            node: {
                type: Object,
                required: true,
                default: () => ({})
            },
            isLast: {
                type: Boolean,
                default: false
            }
        },
        emits: ['toggle-expand', 'show-params'],
        setup(props, {emit}) {
            // 默认展开深度 < 2（即0和1层，共两层）
            const getExpanded = () => {
                if (props.node.expanded === undefined) {
                    return props.node.depth < 2;
                }
                return props.node.expanded;
            };

            const hasChildren = ref(
                props.node.children && Array.isArray(props.node.children) && props.node.children.length > 0
            );

            const handleToggle = () => {
                if (hasChildren.value) {
                    const newState = !getExpanded();
                    props.node.expanded = newState;
                    emit('toggle-expand', props.node.spanId, newState);
                }
            };

            // 双击打开节点详情
            const handleDblClickNode = () => {
                window.$app.$emit('node-click', props.node);
            };

            // 点击参数按钮事件
            const handleShowParams = (e) => {
                e.stopPropagation(); // 阻止事件冒泡到节点双击
                emit('show-params', props.node.parameters || []);
            };

            return () => {
                const node = props.node;
                const expanded = getExpanded();
                const nodeDepth = node.depth || 0;
                // 核心优化：只要节点有参数（parameters存在且非空数组）就显示按钮
                const showParamBtn = node.parameters
                    && Array.isArray(node.parameters)
                    && node.parameters.length > 0;

                return h('div', {
                    class: 'tree-node',
                    'data-depth': nodeDepth,
                    key: `${node.spanId || node.id}-${nodeDepth}`
                }, [
                    // 节点内容 - 双击事件
                    h('div', {
                        class: 'node-wrapper',
                        onDblclick: handleDblClickNode
                    }, [
                        // Element展开/折叠按钮
                        hasChildren.value && h(ElButton, {
                            type: 'text',
                            size: 'mini',
                            style: {
                                'padding': '4px 8px',
                                'border-radius': '4px',
                                'background-color': 'rgba(24, 144, 255, 0.1)',
                                'color': '#1890ff',
                                'min-width': '60px',
                                'transition': 'all 0.2s'
                            },
                            onClick: (e) => {
                                e.stopPropagation();
                                handleToggle();
                            }
                        }, expanded ? '折叠' : '展开'),

                        // 节点状态图标
                        h('div', {
                            class: ['node-icon',
                                node.sqlId ? 'node-icon-sql' :
                                    node.success ? 'node-icon-success' : 'node-icon-error'
                            ]
                        }),

                        // 节点核心信息
                        h('div', {class: 'node-main'}, [
                            h('div', {class: 'node-title'},
                                `${node.serviceName || '未知服务'}.${node.methodName || '未知方法'}`
                            ),
                            h('div', {class: 'node-meta'}, [
                                h('div', {class: 'meta-item'}, [
                                    h('span', {class: 'meta-key'}, '耗时:'),
                                    h('span', {
                                        class: ['meta-value', node.costTime > 1000 ? 'cost-highlight' : '']
                                    }, `${node.costTime || 0}ms`)
                                ]),
                                h('div', {class: 'meta-item'}, [
                                    h('span', {class: 'meta-key'}, '线程:'),
                                    h('span', {class: 'meta-value'}, node.threadName || '未知线程')
                                ]),
                                node.sqlId && h('div', {class: 'meta-item'}, [
                                    h('span', {class: 'meta-key'}, 'SQL:'),
                                    h('span', {class: 'meta-value'}, node.sqlId.split('.').pop())
                                ])
                            ])
                        ]),

                        // 有参数的节点均显示参数按钮
                        showParamBtn && h(ElButton, {
                            type: 'primary',
                            size: 'mini',
                            class: 'param-btn',
                            onClick: handleShowParams
                        }, '参数')
                    ]),

                    // 递归渲染子节点（修正事件传递逻辑，确保参数能正确传递）
                    hasChildren.value && expanded &&
                    node.children.map((child, index) => {
                        const isLastChild = index === node.children.length - 1;
                        return h(TreeNode, {
                            node: child,
                            isLast: isLastChild,
                            onToggleExpand: (spanId, state) => emit('toggle-expand', spanId, state),
                            onShowParams: (params) => emit('show-params', params), // 关键修复：显式传递参数
                            key: `${child.spanId || child.id}-${index}-${nodeDepth + 1}`
                        });
                    })
                ]);
            };
        }
    });

    // 保持其他逻辑不变
    const calculateTreeStats = (treeData) => {
        const stats = {
            totalNodes: 0,
            maxDepth: 0,
            sqlNodes: 0
        };

        const traverse = (nodes) => {
            nodes.forEach(node => {
                stats.totalNodes++;
                const nodeDepth = node.depth || 0;
                if (nodeDepth > stats.maxDepth) {
                    stats.maxDepth = nodeDepth;
                }
                if (node.sqlId) {
                    stats.sqlNodes++;
                }
                if (node.children && Array.isArray(node.children) && node.children.length > 0) {
                    traverse(node.children);
                }
            });
        };

        traverse(treeData);
        return stats;
    };

    const deepClone = (obj) => {
        return JSON.parse(JSON.stringify(obj));
    };

    const app = createApp({
        components: {
            ElForm, ElFormItem, ElInput, ElButton,
            ElTable, ElTableColumn, ElPagination, ElDialog,
            ElDescriptions, ElDescriptionsItem, ElTag, ElSelect, ElOption, ElDatePicker,
            TreeNode
        },
        setup() {
            window.$app = {
                $emit: (event, data) => {
                    if (event === 'node-click') {
                        currentNode.value = deepClone(data);
                        nodeDetailVisible.value = true;
                    }
                }
            };

            const tableData = ref([]);
            const totalCount = ref(0);
            const loading = ref(false);
            const loadingTree = ref(false);
            const treeData = ref([]);
            const currentTraceId = ref('');
            const isAllExpanded = ref(false);
            const expandDepth = ref(2);
            const treeStats = ref({totalNodes: 0, maxDepth: 0, sqlNodes: 0});
            // 日期范围
            const dateRange = ref([]);
            const trace_monitor_parameterSwitch = ref(false);
            const trace_monitor_enabled = ref(false);
            const traceDialogVisible = ref(false);
            const nodeDetailVisible = ref(false);
            const currentNode = ref(null);

            // 参数弹框相关状态 - 默认关闭，避免自动弹出
            const paramDialogVisible = ref(false);
            const requestParams = ref([]);
            const responseParam = ref(null);

            // 级联下拉相关数据
            const serviceList = ref([]); // 所有服务数据
            const filteredServiceList = ref([]); // 过滤后的服务列表
            const allMethodList = ref([]); // 所有服务的所有方法（无服务类名时使用）
            const filteredMethodList = ref([]); // 过滤后的方法列表

            const queryParams = reactive({
                size: 10,
                from: 0,
                serviceName: '',
                methodName: '',
                traceId: ''
            });
            const pickerOptions = reactive([{
                    text: '过去3分钟',
                    value: () => {
                        const end = new Date()
                        const start = new Date()
                        start.setMinutes(start.getMinutes() - 3) // 减去3分钟
                        return [start, end]
                    },
                },
                    {
                        text: '过去5分钟',
                        value: () => {
                            const end = new Date()
                            const start = new Date()
                            start.setMinutes(start.getMinutes() - 5) // 减去5分钟
                            return [start, end]
                        },
                    },
                    {
                        text: '过去10分钟',
                        value: () => {
                            const end = new Date()
                            const start = new Date()
                            start.setMinutes(start.getMinutes() - 10) // 减去10分钟
                            return [start, end]
                        },
                    }, {
                        text: '过去20分钟',
                        value: () => {
                            const end = new Date()
                            const start = new Date()
                            start.setMinutes(start.getMinutes() - 20) // 减去20分钟
                            return [start, end]
                        },
                    }, {
                        text: '过去30分钟',
                        value: () => {
                            const end = new Date()
                            const start = new Date()
                            start.setMinutes(start.getMinutes() - 30) // 减去30分钟
                            return [start, end]
                        },
                    }, {
                        text: '过去60分钟',
                        value: () => {
                            const end = new Date()
                            const start = new Date()
                            start.setMinutes(start.getMinutes() - 60) // 减去60分钟
                            return [start, end]
                        },
                    },]
            )

            const currentPage = ref(1);

            onMounted(() => {
                fetchSwitch_Get('trace-monitor-enabled');
                fetchSwitch_Get('trace-monitor-parameterSwitch');
                dateRangeChange();
                fetchRootNodes().catch(err => {
                    console.error('加载根节点失败:', err);
                    ElMessage.error('加载数据失败，请重试');
                });
                // 加载服务类名和方法名数据
                getServiceData();
            });

            // 获取服务和方法数据
            const getServiceData = async () => {
                try {
                    let url = window.location.href.replace('/trace-tree.html', '/api/trace-monitor/getEntranceServiceInfo');
                    const res = await axios.get(url);
                    serviceList.value = res.data || [];
                    filteredServiceList.value = [...serviceList.value];

                    // 收集所有服务的所有方法，添加服务类名关联字段
                    allMethodList.value = [];
                    serviceList.value.forEach(service => {
                        const methods = service.methodInfoList || [];
                        const serviceMethods = methods.map(method => ({
                            ...method,
                            serviceName: service.serviceName, // 关联所属服务类名
                            serviceDes: service.serviceDes // 关联服务描述
                        }));
                        allMethodList.value.push(...serviceMethods);
                    });

                    // 初始化方法列表为所有方法
                    filteredMethodList.value = [...allMethodList.value];
                } catch (err) {
                    console.error('获取服务数据失败:', err);
                    ElMessage.error('获取服务列表失败，请刷新重试');
                }
            };

            // 服务类名模糊搜索
            const filterServiceName = (query) => {
                if (!query) {
                    filteredServiceList.value = [...serviceList.value];
                    return;
                }
                const lowerQuery = query.toLowerCase();
                filteredServiceList.value = serviceList.value.filter(service =>
                    service.serviceName.toLowerCase().includes(lowerQuery)
                    || (service.serviceDes && service.serviceDes.toLowerCase().includes(lowerQuery))
                );
            };

            // 服务类名改变时更新方法列表（联动逻辑保留）
            const handleServiceChange = (serviceName) => {
                // 保留联动：选择服务类名后，方法列表过滤为该服务的方法
                if (serviceName) {
                    const selectedService = serviceList.value.find(
                        item => item.serviceName === serviceName
                    );
                    const serviceMethods = selectedService
                        ? selectedService.methodInfoList.map(method => ({
                            ...method,
                            serviceName: selectedService.serviceName,
                            serviceDes: selectedService.serviceDes
                        }))
                        : [];
                    filteredMethodList.value = serviceMethods;
                } else {
                    // 无服务类名时，显示所有方法
                    filteredMethodList.value = [...allMethodList.value];
                }
            };

            // 方法名模糊搜索（支持无服务类名时搜索所有方法）
            const filterMethodName = (query) => {
                const currentService = queryParams.serviceName;
                let sourceMethods = [];

                // 有服务类名：只搜该服务的方法；无服务类名：搜所有方法
                if (currentService) {
                    const selectedService = serviceList.value.find(
                        item => item.serviceName === currentService
                    );
                    sourceMethods = selectedService
                        ? selectedService.methodInfoList.map(method => ({
                            ...method,
                            serviceName: selectedService.serviceName,
                            serviceDes: selectedService.serviceDes
                        }))
                        : [];
                } else {
                    sourceMethods = [...allMethodList.value];
                }

                if (!query) {
                    filteredMethodList.value = sourceMethods;
                    return;
                }

                const lowerQuery = query.toLowerCase();
                filteredMethodList.value = sourceMethods.filter(method =>
                    method.methodName.toLowerCase().includes(lowerQuery)
                    || (method.methodDes && method.methodDes.toLowerCase().includes(lowerQuery))
                    || (method.serviceName && method.serviceName.toLowerCase().includes(lowerQuery)) // 支持按服务名过滤方法
                );
            };

            const dateRangeChange = async () => {
                const end = new Date()
                const start = new Date()
                start.setHours(start.getHours() - 6) // 减去6小时
                dateRange.value = [start, end]
                handleDateChange(dateRange.value);
            };
            const fetchRootNodes = async () => {
                try {
                    loading.value = true;
                    let url = window.location.href.replace('/trace-tree.html', '/api/trace-monitor/getRootNodes');
                    const response = await axios.post(url, queryParams);
                    const pageRes = response.data || {traceNodeList: [], totalCount: 0};
                    tableData.value = pageRes.traceNodeList || [];
                    totalCount.value = pageRes.totalCount || 0;
                } catch (error) {
                    console.error('获取根节点失败:', error);
                    ElMessage.error('获取数据失败，请重试');
                    tableData.value = [];
                    totalCount.value = 0;
                } finally {
                    loading.value = false;
                }
            };
            const fetchSwitch_Set = async (enableKey) => {
                try {
                    loading.value = true;
                    let url = window.location.href.replace('/trace-tree.html', '/api/trace-monitor/setMonitorEnabled');

                    let enabledValue = '';

                    if (enableKey === 'trace-monitor-enabled') {
                        enabledValue = trace_monitor_enabled.value ? 'on' : 'off';
                    }
                    if (enableKey === 'trace-monitor-parameterSwitch') {
                        enabledValue = trace_monitor_parameterSwitch.value ? 'on' : 'off';
                    }
                    const response = await axios.get(url, {
                        params: {enableKey, enabledValue}
                    });
                    const switch_enable = response.data;
                    if (enableKey === 'trace-monitor-enabled') {
                        trace_monitor_enabled.value = switch_enable === "on";
                    }
                    if (enableKey === 'trace-monitor-parameterSwitch') {
                        trace_monitor_parameterSwitch.value = switch_enable === "on";
                    }
                } catch (error) {
                    console.error('设置开关状态失败:', error);
                    ElMessage.error('设置开关状态失败，请重试');
                    treeData.value = [];
                } finally {
                    loading.value = false;
                }
            };

            const fetchSwitch_Get = async (enableKey) => {
                try {
                    loading.value = true;
                    let url = window.location.href.replace('/trace-tree.html', '/api/trace-monitor/getMonitorEnabled');
                    const response = await axios.get(url, {
                        params: {enableKey}
                    });
                    const switch_enable = response.data;
                    if (enableKey === 'trace-monitor-enabled') {
                        trace_monitor_enabled.value = switch_enable === "on";
                    }
                    if (enableKey === 'trace-monitor-parameterSwitch') {
                        trace_monitor_parameterSwitch.value = switch_enable === "on";
                    }

                } catch (error) {
                    console.error('获取开关状态失败:', error);
                    ElMessage.error('获取开关状态失败，请重试');
                    treeData.value = [];
                } finally {
                    loadingTree.value = false;
                }
            };
            const fetchTraceDetail = async (traceId) => {
                try {
                    loadingTree.value = true;
                    currentTraceId.value = traceId;
                    let url = window.location.href.replace('/trace-tree.html', '/api/trace-monitor/getTraceById');
                    const response = await axios.get(url, {
                        params: {traceId}
                    });
                    const rawTreeData = response.data || [];
                    treeData.value = deepClone(rawTreeData);
                    treeStats.value = calculateTreeStats(treeData.value);
                    isAllExpanded.value = false;
                    traceDialogVisible.value = true;
                    paramDialogVisible.value = false;
                } catch (error) {
                    console.error('获取调用链详情失败:', error);
                    ElMessage.error('获取调用链失败，请重试');
                    treeData.value = [];
                } finally {
                    loadingTree.value = false;
                }
            };
            const toggleAllExpand = () => {
                const newState = !isAllExpanded.value;
                isAllExpanded.value = newState;

                const updateAllNodes = (nodes) => {
                    nodes.forEach(node => {
                        if (node.children && Array.isArray(node.children) && node.children.length > 0) {
                            node.expanded = newState;
                            updateAllNodes(node.children);
                        }
                    });
                };

                updateAllNodes(treeData.value);
                treeData.value = [...treeData.value];
            };

            const expandToDepth = () => {
                const targetDepth = Number(expandDepth.value) || 2;
                isAllExpanded.value = false;

                const updateNodesByDepth = (nodes, currentDepth = 0) => {
                    nodes.forEach(node => {
                        if (node.children && Array.isArray(node.children) && node.children.length > 0) {
                            node.expanded = currentDepth < targetDepth;
                            updateNodesByDepth(node.children, currentDepth + 1);
                        }
                    });
                };

                updateNodesByDepth(treeData.value);
                treeData.value = [...treeData.value];
                ElMessage.success(`已展开至第${targetDepth}层`);
            };

            const handleNodeToggle = (spanId, state) => {
                const findAndUpdateNode = (nodes) => {
                    for (const node of nodes) {
                        if (node.spanId === spanId) {
                            node.expanded = state;
                            return true;
                        }
                        if (node.children && Array.isArray(node.children) && node.children.length > 0) {
                            if (findAndUpdateNode(node.children)) return true;
                        }
                    }
                    return false;
                };

                findAndUpdateNode(treeData.value);
                treeData.value = [...treeData.value];
            };

            // 处理参数弹框显示（仅通过按钮点击触发）
            const handleShowParams = (parameters) => {
                requestParams.value = [];
                responseParam.value = null;
                requestParams.value = (parameters || []).filter(p => p.type === 1);
                responseParam.value = (parameters || []).find(p => p.type === 2) || null;
                paramDialogVisible.value = true;
            };

            const closeNodeDetail = () => {
                nodeDetailVisible.value = false;
                currentNode.value = null;
            };

            const copyToClipboard = (text, label) => {
                if (!text) {
                    ElMessage.warning(`${label}内容为空，无法复制`);
                    return;
                }
                navigator.clipboard.writeText(text).then(() => {
                    ElMessage.success(`${label}已成功复制`);
                }).catch(() => {
                    ElMessage.error(`${label}复制失败，请手动复制`);
                });
            };

            const copyTraceId = () => {
                copyToClipboard(currentTraceId.value, 'TraceId');
            };

            const formatDateTime = (timestamp) => {
                if (!timestamp) return '未知时间';
                const date = new Date(timestamp);
                return date.toLocaleString('zh-CN', {
                    year: 'numeric', month: '2-digit', day: '2-digit',
                    hour: '2-digit', minute: '2-digit', second: '2-digit',
                    fractionalSecondDigits: 3
                });
            };

            const handleQuery = () => {
                currentPage.value = 1;
                queryParams.from = 0;
                fetchRootNodes();
            };

            const resetQuery = () => {
                Object.assign(queryParams, {
                    size: 10, from: 0,
                    serviceName: '', methodName: '', traceId: ''
                });
                currentPage.value = 1;
                // 重置下拉框状态：服务类名清空，方法列表恢复为所有方法
                filteredServiceList.value = [...serviceList.value];
                filteredMethodList.value = [...allMethodList.value];
            };

            const handleSizeChange = (size) => {
                queryParams.size = size;
                queryParams.from = 0;
                currentPage.value = 1;
                fetchRootNodes();
            };
            // 处理日期范围变化
            const handleDateChange = (val) => {
                if (val && val.length === 2) {
                    queryParams.startTime_began = val[0].getTime();
                    queryParams.startTime_end = val[1].getTime();
                } else {
                    queryParams.startTime_began = null;
                    queryParams.startTime_end = null;
                }
            };
            const handleCurrentChange = (page) => {
                currentPage.value = page;
                queryParams.from = (page - 1) * queryParams.size;
                fetchRootNodes();
            };

            const handleViewTrace = (traceId) => {
                fetchTraceDetail(traceId);
            };

            return {
                tableData,
                totalCount,
                loading,
                loadingTree,
                treeData,
                queryParams,
                currentPage,
                currentTraceId,
                isAllExpanded,
                expandDepth,
                treeStats,
                dateRange,
                pickerOptions,
                trace_monitor_enabled,
                trace_monitor_parameterSwitch,
                traceDialogVisible,
                nodeDetailVisible,
                currentNode,
                paramDialogVisible,
                requestParams,
                responseParam,
                serviceList,
                filteredServiceList,
                allMethodList,
                filteredMethodList,
                handleShowParams,
                handleQuery,
                resetQuery,
                handleSizeChange,
                handleDateChange,
                handleCurrentChange,
                handleViewTrace,
                toggleAllExpand,
                expandToDepth,
                handleNodeToggle,
                copyTraceId,
                formatDateTime,
                closeNodeDetail,
                copyToClipboard,
                fetchSwitch_Set,
                fetchSwitch_Get,
                dateRangeChange,
                filterServiceName,
                handleServiceChange,
                filterMethodName,
                getServiceData
            };
        }
    });

    app.component('TreeNode', TreeNode);
    app.use(ElementPlus).mount('#app');
</script>
</body>
</html>